package com.bub.pay.framework.common.util;

import com.bub.pay.framework.common.constant.SystemConstants;
import com.bub.pay.framework.common.entity.model.UserSession;
import com.bub.pay.framework.common.exception.GlobalException;
import com.bub.pay.framework.common.entity.model.SessionModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.MDC;
import org.springframework.beans.BeanUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import static com.bub.pay.framework.common.constant.SystemConstants.USER_INFO;
import static com.bub.pay.framework.common.base.Cause.unlogin;

@Slf4j
public class ContextHolder {


    /**
     * 获取用户Session
     *
     * @return UserSession
     */
    public static UserSession getUserSession() {

        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession(false);
        if (session == null) {
            throw new GlobalException(unlogin);
        }

        SessionModel sessionModel = (SessionModel) session.getAttribute(USER_INFO);
        if (sessionModel == null) return null;
        UserSession userSession = new UserSession();
        BeanUtils.copyProperties(sessionModel, userSession);
        return userSession;
    }


    public static SessionModel currentUserInfo() {

        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession(false);
        if (session == null) {
            throw new GlobalException(unlogin);
        }

        SessionModel sessionModel = (SessionModel) session.getAttribute(USER_INFO);
        return sessionModel;
    }


    public static HttpServletRequest getServletRequest() {
        Map<String, Object> requestDataMap = getRequestDataMap();
        HttpServletRequest httpServletRequest = (HttpServletRequest) requestDataMap.get("servletRequest");
        if (httpServletRequest == null) {
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            httpServletRequest = requestAttributes.getRequest();
            setServletRequest(httpServletRequest);
            return httpServletRequest;
        }
        return httpServletRequest;
    }

    public static void setServletRequest(HttpServletRequest httpServletRequest) {
        Map<String, Object> requestDataMap = getRequestDataMap();
        requestDataMap.put("servletRequest", httpServletRequest);
    }

    public static HttpServletResponse getServletResponse() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return requestAttributes.getResponse();
    }

    public static HttpSession getSession() {
        return getServletRequest().getSession();
    }

    /**
     * 向当前response写入文本
     *
     * @param text 文本
     */
    public static void writeCurrentResponse(String text) {
        HttpServletResponse response = getServletResponse();
        response.setCharacterEncoding("UTF-8");
        try (PrintWriter out = response.getWriter()) {
            out.print(text);
        } catch (IOException e) {
            log.error("写入响应异常", e);
        }
    }

    private static ThreadLocal<Map<String, Object>> requestThreadLocal = new ThreadLocal<>();

    private static Map<String, Object> getRequestDataMap() {
        Map<String, Object> requestDataMap = requestThreadLocal.get();
        if (requestDataMap == null) {
            requestDataMap = new HashMap<>();
            requestThreadLocal.set(requestDataMap);
        }
        return requestDataMap;
    }

    /**
     * 获取请求数据
     * 如果查询字符串有 则 取查询字符串
     * 否则取请求体
     *
     * @return 请求体内容
     */
    public static String getRequestData() {
        Map<String, Object> requestDataMap = getRequestDataMap();
        String requestData = (String) requestDataMap.get("requestData");
        if (StringUtils.isNotBlank(requestData)) {
            return requestData;
        }
        HttpServletRequest servletRequest = ContextHolder.getServletRequest();
        requestData = servletRequest.getQueryString();
        if (StringUtils.isEmpty(requestData)) {
            try {
                requestData = IOUtils.toString(servletRequest.getInputStream(), SystemConstants.DEFAULT_CODE);
            } catch (IOException e) {
                log.error("error", e);
                e.printStackTrace();
            }
        }
        requestDataMap.put("requestData", requestData);
        return requestData;
    }

    /**
     * 设置请求数据
     *
     * @param requestData 请求数据
     */
    public static void setRequestData(String requestData) {
        Map<String, Object> requestDataMap = getRequestDataMap();
        requestDataMap.put("requestData", requestData);
    }

    /**
     * 获取当前认证token
     *
     * @return 当前认证token
     */
    public static String getAuthToken() {
        Map<String, Object> requestDataMap = getRequestDataMap();
        String authorization = (String) requestDataMap.get("authToken");
        if (StringUtils.isBlank(authorization)) {
            HttpServletRequest servletRequest = getServletRequest();
            authorization = servletRequest.getHeader("Authorization");
            requestDataMap.put("authToken", authorization);
        }
        return authorization;
    }

    /**
     * 设置认证token
     *
     * @param authToken 认证token
     */
    public static void setAuthToken(String authToken) {
        Map<String, Object> requestDataMap = getRequestDataMap();
        requestDataMap.put("authToken", authToken);
    }

    /**
     * 清理请求数据
     */
    public static void clearRequestDataMap() {
        Map<String, Object> requestMap = requestThreadLocal.get();
        if (requestMap != null) {
            requestMap.clear();
        }
        log.info("clearRequestDataMap");
        requestThreadLocal.remove();
        MDC.clear();
    }

    /**
     * 设置
     *
     * @param key key
     * @param val value
     */
    public static void set(String key, Object val) {
        Map<String, Object> requestDataMap = getRequestDataMap();
        requestDataMap.put(key, val);
    }

    /**
     * 获取值
     *
     * @param key key
     * @param <T> 返回值泛型
     * @return 值
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(String key) {
        Map<String, Object> requestDataMap = getRequestDataMap();
        return (T) requestDataMap.get(key);
    }
}
